/*Para Compilar: mpicc fpm.c -o fpm*/
/*Para Rodar: mpirun -np x fpm; onde x é o número de threads*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <omp.h>
#include <mpi.h>

#define TAM_PALAVRA 50
#define TAM_FRASE 100
#define TAM_BLOCO 200

int busca_palindromo(char *s);
int verificador(char *s);

int main (int argc, char *argv[]){
   	int i = 0, rankTh = 0,qtdTh = 0, trd = 1, id =0, cont1 = 0, cont2 = 0;
   	char *bloco, *palavra, *frase;
   	int contPa = 0, contFr = 0;
	long tamArq = 0;
	char *tokenPa,*tokenFr;

	MPI_Init(&argc,&argv);//Inicializa os processos
        MPI_Comm_rank(MPI_COMM_WORLD, &rankTh);//Retorna o rank do processo
        MPI_Comm_size(MPI_COMM_WORLD, &qtdTh);//retorna a quantidade de processos
        MPI_Status status;//Retorna o status do processo

	
	if(rankTh == 0){//Bloco Atua se  o rank do processo for igual a 0
   		FILE *arq_grand;

   		if(!(arq_grand = fopen("shakespe.txt","r"))){
			printf("Erro na abertura do arquivo.");
			exit(-1);		
		}

		fseek(arq_grand, 0, SEEK_END);//Diz o tamanho do arquivo para ser divido pelos processos
		tamArq = ftell(arq_grand);
		rewind(arq_grand);

		if (qtdTh == 1) {
			MPI_Finalize();
			return -1;
		}
                else 
		        tamArq = tamArq/(qtdTh - 1) + 10;

		bloco = (char*) malloc(tamArq * sizeof(char));

		while(!feof(arq_grand)){//Le do arquivo e manda uma parte para cada processo	
			
			fread(bloco, 1, tamArq, arq_grand);
			
			id = trd;
			MPI_Send(&tamArq, 1, MPI_INT, trd, id, MPI_COMM_WORLD);

			id = trd+1;
			MPI_Send(bloco, tamArq, MPI_CHAR, trd, id, MPI_COMM_WORLD);
			
			trd++;
		}

		for(i = 1;i < qtdTh;i++){//Recebe as informações que cada processo computou e apresenta os resultados
			id = 2;
			MPI_Recv(&cont1,1,MPI_INT,i,i+id,MPI_COMM_WORLD,&status);
			contFr = contFr + cont1;
			id = 3;	
			MPI_Recv(&cont2,1,MPI_INT,i,i+id,MPI_COMM_WORLD,&status);
			contPa = contPa + cont2;
		}
		printf("No de Frases Palindromos: %d\nNo de Palavras Palindromos: %d\n",contFr,contPa);
 		
		fclose(arq_grand);
	} else {//Bloco atua se  o rank do processo for igual a 1 ou mais		
		id = rankTh;
		MPI_Recv(&tamArq, 1, MPI_INT, 0, id, MPI_COMM_WORLD, &status);//Recebe o tamanho do bloco do processo 0		

		bloco = (char *) malloc(tamArq * sizeof(char));
				
		id = rankTh+1;		
		MPI_Recv(bloco, tamArq, MPI_CHAR, 0, id, MPI_COMM_WORLD, &status);//Recebe o bloco do processo 0
		
		frase = strtok_r(bloco,".\t\n\r",&tokenFr);

		while(frase != NULL){//Enquanto nao terminar o bloco, pega-se uma frase, verifica se essa frase e palindromo, depois 
                                     //separa- a em palavras e ve se cada palavra é um palindromo e, se for, ve se ela é primo.
			if (verificador(frase))
					cont1++;
			palavra = strtok_r(frase, " ,/?'\";:|^-!$#@`~*&%)(+=_}{][\\",&tokenPa);

			while (palavra != NULL){
				if (verificador(palavra))
					cont2++;				
				palavra = strtok_r(NULL, " ,/?'\";:|^-!$#@`~*&%)(+=_}{][\\",&tokenPa);
			}
			frase = strtok_r(NULL,".\t\n\r",&tokenFr);
		}
		id = 2;
		MPI_Send(&cont1, 1, MPI_INT, 0, id+rankTh, MPI_COMM_WORLD);//manda a quantidade de frases que são palindromos

		id = 3;
		MPI_Send(&cont2, 1, MPI_INT, 0, id+rankTh, MPI_COMM_WORLD);//manda a quantidade de palavras que são palindromos

		free(bloco);
	}	
	MPI_Finalize();
   //----------------------------------------------
   //----------------------------------------------
   return 0;
}
//--------------------------------------------------
//--------------------------------------------------
//--------------------------------------------------
/**converte para minusculo se necessário, verifica já se é palindromo**/
/**retorna o valor total em ascii**/
int verificador(char *s){
    	int i = 0, j = 0, test = 0,aux = 0;
    	char *bloco;

	aux = strlen(s);

	bloco  = (char*)malloc(aux*sizeof(char));

    	for(i=0; i < strlen(s); i++){
        	if (isupper(s[i])){ /**maiusculo->minusculo**/
            		s[i] += 32;
            		bloco[j++] = s[i];
        	}
        	else if(isdigit(s[i]) || islower(s[i])){ /**verifica se já numeros no meio ou se já é minuscula **/
        		bloco[j++] = s[i];
        	}
		else
        		bloco[j] = '\0'; //coloca final de string
    	}

  	test = busca_palindromo(bloco);

   	return test;
}

/**verifica se a palavra é palindrmo e já converte para ascii**/
int busca_palindromo(char *s){
   	int i, l = 0, count = 0;
   	l = strlen(s);

  	if (l == 0)
     		return 0;

   	for(i = 0; i < l/2; i++){ /**para palindromo par**/
     		if ( s[i] != s[l-i-1] ) /** se já for diferente o inicio e o fim retorna 0 **/
         		return 0;
       		/**senão vai somando o inicio e o fim da palavra e convertendo para ascii**/
        	count += s[i];
        	count += s[l-i-1];
   	}
   	if(l%2 == 1)/**para palindromo impar soma o termo do meio**/
     		return (count + s[1+l/2]);

   	else
     		return count;
}

